/*  

* Copyright (C) 2013 - 2016 Xilinx, Inc
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.

*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License along
*   with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
#include <linux/exportfs.h>
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/of_dma.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/of_reserved_mem.h>
#include <asm/irq.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

/*
本文件适用于内存缓存帧数为1的情况，即一次传输一帧数据。
*/


// 用户参数修改区域
#define WIDTH 1920
#define HEIGHT 1200
#define PIC_CHANNAL 3 
#define TRIGGER_PERIOD 50000000 // 50ms
// #define AUTO_TRIGGER            // 自动触发,注释掉则为手动触发

#define MAX_SIZE (WIDTH*HEIGHT*PIC_CHANNAL)
#define DEVICE_COUNT		1
#define DEVICE_NAME		"vdma0"
#define CAMERA_RESET 0
#define CAMERA_TRIGGER 1
#define MIPI_CAM_IRQ  _IOR('m',1,int)


//字符设备结构体
struct vdma_dev
{
	dev_t 	devid;							//设备号
	int major;								//主设备号
	int minor;								//副设备号
	struct cdev	cdev;						//字符设备
	struct class *class;					//设备类
	struct device *device;					//设备节点
	struct dma_chan  *rx_chan;				//dma通道
	struct dma_device *rx_dev; 				//dma设备
	struct scatterlist  rx_sg;				//dma scatterlist
    struct dma_async_tx_descriptor *rxd;	//传输描述符  
    enum dma_ctrl_flags flags; 				//传输标志位
	struct xilinx_vdma_config config;		//vdma配置信息
	struct dma_interleaved_template xt;		//dma模板
	dma_cookie_t rx_cookie;					//dma cookie
	wait_queue_head_t read_queue;			//读队列
	int irq_reprot;							//中断标志位
	int read_mem; 							//指向要读取的帧缓冲区

	// 相机触发信号相关
	struct hrtimer htimer; 					//高精度定时器
	ktime_t htimeperiod;					//定时器周期
	struct device_node * cam_node;			//相机设备节点
	int cam_emio[2];						//相机复位和触发信号引脚

	// 中断相关
	struct device_node* pdev_irq_node;
	unsigned int dev_irq_num;
};


dma_addr_t dma_dst;			//源地址和目的地址 
unsigned char *dst;
phys_addr_t dst_phy;
u32 temp1; 
int cc = 7;

struct vdma_dev vdma;
struct dma_tx_state dma_status;

static DECLARE_WAIT_QUEUE_HEAD(vdma_tri_waitq);
static volatile unsigned char  start_trigger_flag = 0;

// 函数声明区域
static int vdma_probe(struct platform_device *dev);
static int vdma_remove(struct platform_device *dev);
void rx_callback(void *dma_async_param);
static int vdmarx_task(struct vdma_dev *vdma);
static irqreturn_t vdma_irq_hander(int irq, void *dev_id);


static int vdma_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &vdma;
	vdma.irq_reprot = 0;
	memset(dst , 0 , MAX_SIZE);

    //7.申请PL->PS中断
	//与设备树中的节点CAM_IRQ匹配
	vdma.pdev_irq_node = of_find_compatible_node(NULL, NULL, "MIPI_CAM_IRQ");
	if(vdma.pdev_irq_node==NULL)
	{
		printk("can't find the MIPI_CAM_IRQ device \r\n");
	}
	else
	{
		printk("the device name is %s\r\n",vdma.pdev_irq_node->name);
	}
	vdma.dev_irq_num = irq_of_parse_and_map(vdma.pdev_irq_node, 0);
	//申请中断
	//irq_number     irq_function    trigger_mode                   irq_name
	int ret1=request_irq(vdma.dev_irq_num,  vdma_irq_hander,  IRQF_TRIGGER_RISING,"MIPI_CAM_IRQ",NULL);
	if(ret1)
	{
		printk("request MIPI_CAM_IRQ failed! ret = %d\r\n", ret1);
		return -1;
	}
	printk("VDMA0 open!\r\n");
	return 0;
}


static int vdma_release(struct inode *inode, struct file *filp)
{
	struct vdma_dev *vdma =  filp->private_data;
	free_irq(vdma->dev_irq_num,NULL);
    dmaengine_terminate_all(vdma->rx_chan);
	filp->private_data = NULL;
	printk("VDMA0 release!\r\n");
	return 0;

}

static ssize_t vdma_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{
	// int ret;
	// char writebuf;
	// 如果没有设置自动触发，则需要手动触发一次
	// 开启一次vdma传输
	#ifndef AUTO_TRIGGER
	gpio_set_value(vdma.cam_emio[CAMERA_TRIGGER], 0);
	mdelay(1);
	gpio_set_value(vdma.cam_emio[CAMERA_TRIGGER], 1);

	printk("trigger one\r\n");
	#endif

	vdmarx_task(&vdma);

	return 0;
}

static unsigned int vdma_poll(struct file *filp, struct poll_table_struct *wait)
{
	int mask = 0;
	// 等待读队列中有数据
	poll_wait(filp, &(vdma.read_queue), wait);
	if (vdma.irq_reprot == 1)
	{
		vdma.irq_reprot = 0;
		mask |= (POLLIN | POLLRDNORM);
	}
	// 一开始调用时会return 0, 后面再有数据时会被唤醒,return POLLIN | POLLRDNORM
	// printk("return at vdma_poll\r\n");
	return mask;
}




ssize_t vdma_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	// struct vdma_dev *vdma =  filp->private_data;
	int ret = 0;
	ret = copy_to_user(buf, dst, MAX_SIZE);
	if(ret < 0)
	{
		printk("%s %s %d vdma read fail!\r\n",__FILE__,__FUNCTION__,__LINE__);
	}	
	return 0;
}

static long vdma_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
	int tmp;
	if(_IOC_TYPE(cmd)!='m')
	return -ENOTTY ;
	switch(cmd)
	{
		case MIPI_CAM_IRQ:
		{	
			//等待采集信号到来
			printk("flag:%d , waiting tri\r\n", start_trigger_flag);
			wait_event_interruptible(vdma_tri_waitq, (start_trigger_flag==1));
			//清除采集信号
			start_trigger_flag = 0;
			break;
		}
		default: break;
    }
	return tmp;
}

static int vdma_mmap(struct file *pfile, struct vm_area_struct *vma)
{
	int ret = 0;
	vma->vm_flags |= (VM_IO | VM_LOCKED | VM_DONTEXPAND | VM_DONTDUMP);
	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
	ret = remap_pfn_range(vma,							   /* 映射虚拟内存空间 */
						  vma->vm_start,				   /* 映射虚拟内存空间起始地址 */
						   dma_dst >> PAGE_SHIFT, /* 与物理内存对应的页帧号，物理地址右移12位 */
						  (vma->vm_end - vma->vm_start),   /* 映射虚拟内存空间大小,页大小的整数倍 */
						  vma->vm_page_prot);			   /* 保护属性 */
	//virt_to_phys(dst)
	if (ret < 0){
		printk("vdma0 mmap fail!\r\n");
		return -EIO;
	}
	return 0;
}

static const struct file_operations vdma_fops = {
	.owner			= THIS_MODULE,
	.open			= vdma_open,
	.read			= vdma_read,
	.write      	= vdma_write,
	.poll 			= vdma_poll,
	.unlocked_ioctl = vdma_ioctl,	
	.mmap			= vdma_mmap,	
	.release		= vdma_release,
};


// 定时器回调函数
static enum hrtimer_restart hrtimer_hander(struct hrtimer *timer)
{
	gpio_set_value(vdma.cam_emio[CAMERA_TRIGGER], 1);
	mdelay(1);
	gpio_set_value(vdma.cam_emio[CAMERA_TRIGGER], 0);
	hrtimer_forward_now(&vdma.htimer, vdma.htimeperiod);
	
	// hrtimer_forward(&vdma.htimer,&vdma.htimer->base->get_time(),vdma.htimeperiod);//hrtimer_forward(timer, now, tick_period);
	return HRTIMER_RESTART; //重启定时器
}
// 中断触发回调函数
static irqreturn_t vdma_irq_hander(int irq, void *dev_id)
{
	//PL端触发PS中断，表示已触发,准备传感器采集
	start_trigger_flag=1;
	//唤醒该采集进程
	wake_up_interruptible(&vdma_tri_waitq);
    return IRQ_HANDLED;
}

static int vdma_init(struct vdma_dev *dev)
{
	int ret = 0;
	//1.创建字符设备号
	dev->major = 0;
	if(dev->major)		//给定设备号
	{
		dev->devid = MKDEV(dev->major,0);
		ret = register_chrdev_region(dev->devid,DEVICE_COUNT,DEVICE_NAME);
	}
	else
	{
		ret = alloc_chrdev_region(&dev->devid,0,DEVICE_COUNT,DEVICE_NAME);
	}
	if(ret < 0)
	{
		printk("%s %s %d fail to register!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_register;
	}
	dev->major = MAJOR(dev->devid);
	dev->minor = MINOR(dev->devid);
	printk("VDMA0 MAJOR = %d, VDMA0 MINOR = %d!\r\n", dev->major, dev->minor);

	//2.添加字符设备
	dev->cdev.owner = THIS_MODULE;
	cdev_init(&dev->cdev,&vdma_fops);
	ret = cdev_add(&dev->cdev,dev->devid,DEVICE_COUNT);
	if(ret<0)
	{
		printk("%s %s %d fail to create cdevice!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_cdev;
	}

	//3.自动创建设备节点
	dev->class = class_create(THIS_MODULE, DEVICE_NAME);			//创建类
	if(IS_ERR(dev->class))
	{
		printk("%s %s %d fail to create class!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_class;
	}
	dev->device = device_create(dev->class,NULL,dev->devid,NULL,DEVICE_NAME);	//创建设备
	if(IS_ERR(dev->device))
	{
		printk("%s %s %d fail to create device!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_device;
	}

	//4.获取vdma节点所对应的GPIO
	dev->cam_node = of_find_compatible_node(NULL, NULL, "ar0234");
	dev->cam_emio[CAMERA_TRIGGER] = of_get_named_gpio(dev->cam_node, "CAM_Trigger", 0);
	if (dev->cam_emio[CAMERA_TRIGGER] < 0)
	{
		printk("%s %s %d fail to get name!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_get_name;
	}
	printk("cam_emio[CAMERA_TRIGGER] = %d\r\n", dev->cam_emio[CAMERA_TRIGGER]);

	//5.申请IO
	ret = gpio_request(dev->cam_emio[CAMERA_TRIGGER], "cam_trigger_gpio");
	if (ret){
		printk("fail to request gpio!\r\n");
		goto fail_gpio_request;
	}

	
	

	//6.使用GPIO
	ret = gpio_direction_output(dev->cam_emio[CAMERA_TRIGGER], 0); //设置IO为输出，默认为1高电平触发
	if (ret < 0)
	{
		goto fail_set_output;
	}
	gpio_set_value(vdma.cam_emio[CAMERA_TRIGGER], 0);
	#ifdef AUTO_TRIGGER
	// 设置定时器周期
	dev->htimeperiod = ktime_set(0, TRIGGER_PERIOD); 
	//高精度定时器
	hrtimer_init(&dev->htimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	dev->htimer.function = hrtimer_hander;
	hrtimer_start(&dev->htimer, dev->htimeperiod, HRTIMER_MODE_REL);
	#endif

	


/*
	longmen_vdma_0: longmen_vdma_0@0 {
		compatible ="longmen vdma4";
		dmas = <&axi_vdma_0 1>;
		dma-names = "vdma0_rx";
		xlnx,num-fstores = <0x3>;	//VDMA设备节点中配置的帧缓冲区数
		memory-region = <&vdma_0_reserved>;
	}; 
*/
	printk("VDMA0 init!\r\n");	
	return 0;



fail_set_output:
	gpio_free(dev->cam_emio[CAMERA_TRIGGER]);
fail_gpio_request:
fail_get_name:
	device_destroy(dev->class, dev->devid);
fail_device:
	class_destroy(dev->class);
fail_class:
	cdev_del(&dev->cdev);
fail_cdev:
	unregister_chrdev_region(dev->devid,DEVICE_COUNT);
fail_register:
	printk("%s %s %d VDMA0 fail to init!\r\n",__FILE__,__FUNCTION__,__LINE__);
	return -1;
}

static void vdma_exit(struct vdma_dev *dev)
{
	
	gpio_set_value(dev->cam_emio[CAMERA_TRIGGER], 0);
	gpio_free(dev->cam_emio[CAMERA_TRIGGER]);
	device_destroy(dev->class,dev->devid);						//摧毁设备
	class_destroy(dev->class);									//摧毁类
	cdev_del(&dev->cdev);										//注销字符设备
	unregister_chrdev_region(dev->devid,DEVICE_COUNT);			//注销字符设备号
	printk("VDMA0 exit!\r\n");	
}

static const struct of_device_id vdma_of_match[]={
	{.compatible = "longmen vdma4"},
	{/* Sentinel*/},
};

static struct platform_driver vdma_driver = {
	.driver = {
		.name = "vdma0",
		.of_match_table = vdma_of_match,
	},
	.probe = vdma_probe,
	.remove = vdma_remove,
};

static int vdma_probe(struct platform_device *dev)	
{
	int ret = 0;
	printk("VDMA0 size : height:%d, width:%d\r\n",HEIGHT,WIDTH);
	ret = vdma_init(&vdma);
	if(ret < 0)
	{
		printk("%s %s %d vdma_init fail!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_init;
	}
    dev->dev.coherent_dma_mask = 0xffffffff;	
	//dma_mask与coherent_dma_mask这两个参数表示它能寻址的物理地址的范围,其中dma_coherent_mask则作用于申请一致性DMA缓冲区

	//1.申请通道
	vdma.rx_chan = dma_request_chan(&dev->dev, "vdma0_rx");		//与设备树的dma通道名字相同
	if (vdma.rx_chan == NULL) 
	{
		printk("%s %s %d dma_request_chan fail!\r\n",__FILE__,__FUNCTION__,__LINE__);
		goto fail_rx_chan;
	}
	// vdma.flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;   
	vdma.rx_dev = vdma.rx_chan->device;

	ret = of_reserved_mem_device_init(&dev->dev);
	if(ret < 0)
	{
		printk("%s %s %d fail to init memory, ret = %d!\r\n",__FILE__,__FUNCTION__,__LINE__, ret);
		goto fail_mem_init;
	}

	//2.申请源地址和目标地址
	//static inline dma_addr_t dma_map_single(struct device *dev, void *ptr,size_t size, enum dma_data_direction dir)
	//static inline void *dma_alloc_coherent(struct device *dev, size_t size,dma_addr_t *dma_handle, gfp_t gfp) 
	//dma_alloc_coherent需要soc集成一个cache-coherent-interconnect硬件
	dst = dma_alloc_coherent(&dev->dev,MAX_SIZE, &dma_dst, GFP_KERNEL);//size:  想要申请的内存长度，单位为bytes
	if ( dst== NULL)
    {
		printk("%s %s %d dma_alloc_coherent fail!\r\n",__FILE__,__FUNCTION__,__LINE__);
        ret = -ENOMEM;
		goto fail_dst;
    }
	printk("VDMA0 phy_dma_dst = %llx\r\n",dma_dst);
	printk("VDMA0 virt_dst = %p\r\n", dst);

	memset(&vdma.config, 0, sizeof(struct xilinx_vdma_config));
	/* Set up hardware configuration information */
	vdma.config.park = 1;
	xilinx_vdma_channel_set_config(vdma.rx_chan, &vdma.config);
	init_waitqueue_head(&vdma.read_queue);
	printk("VDMA0 probe!\r\n");

	return 0;
fail_dst:
	of_reserved_mem_device_release(&dev->dev);
fail_mem_init:
	dmaengine_terminate_all(vdma.rx_chan);
	dma_release_channel(vdma.rx_chan);
fail_rx_chan:
	vdma_exit(&vdma);
fail_init:	
	return -1;
}



static int vdmarx_task(struct vdma_dev *vdma)
{
	vdma->flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
	//3.获取传输描述符
	vdma->xt.dst_start = dma_dst;				//第一个块的源总线地址
	vdma->xt.dir = DMA_DEV_TO_MEM;
	vdma->xt.numf = HEIGHT;						//static unsigned int vsize = 32;  要从源读取的字节数
	vdma->xt.sgl[0].size = WIDTH*PIC_CHANNAL;	//static unsigned int hsize = 64;
	vdma->xt.sgl[0].icg = 0;					//此块的最后一个src/dst地址之后和下一块的第一个src/dst地址之前要跳转的字节数
	vdma->xt.frame_size = 1;					//帧中的块数
	vdma->xt.src_sgl     = 0;
	vdma->xt.src_inc     = 0;
	vdma->xt.dst_inc     = 0;
	vdma->xt.dst_sgl     = 0;
	vdma->rxd = vdma->rx_dev->device_prep_interleaved_dma(vdma->rx_chan,&vdma->xt,vdma->flags);
	if(vdma->rxd == NULL)
	{
		printk("%s %s %d device_prep_interleaved_dma fail!\r\n",__FILE__,__FUNCTION__,__LINE__);
		return -1;
	}

	// sg_init_table(&vdma->rx_sg, 1);
	// sg_dma_address(&vdma->rx_sg) = dma_dst;
	// sg_dma_len(&vdma->rx_sg) = MAX_SIZE;
	// vdma->rxd = vdma->rx_dev->device_prep_slave_sg(vdma->rx_chan, &vdma->rx_sg, 1, DMA_DEV_TO_MEM, vdma->flags, NULL);
	// // vdma->rxd = dmaengine_prep_slave_single(vdma->rx_chan, dma_dst, MAX_SIZE, DMA_DEV_TO_MEM, vdma->flags);
	// if (vdma->rxd == NULL)
	// {
	// 	printk("fail to rxd\r\n");
	// 	return -1;
	// }

	vdma->rxd->callback = rx_callback;	//rx通道回调函数，rx通道的device_prep_dma_memcpy任务此函数执行
	vdma->rxd->callback_param = NULL;
	vdma->rx_cookie = vdma->rxd->tx_submit(vdma->rxd);	//提交描述符，执行tx通道的device_prep_dma_memcpy任务

	if (dma_submit_error(vdma->rx_cookie))
	{  
		printk("%s %s %d dma_rx_submit_error fail!\r\n",__FILE__,__FUNCTION__,__LINE__);
    } 	
	dma_async_issue_pending(vdma->rx_chan); 
	printk("dma_async_issue_pending\r\n");
	// while(dma_async_is_tx_complete(vdma->rx_chan, vdma->rx_cookie, NULL, NULL) != DMA_COMPLETE){}
	return 0;
}

void rx_callback(void *dma_async_param)  
{
	vdma.irq_reprot = 1;
	wake_up_interruptible(&vdma.read_queue);
	// for(int i = 0; i< 10;i++){
	// 	printk("%d",*(dst+i));
	// }
	printk("DMA0 transfer complete.\r\n");
} 

static int vdma_remove(struct platform_device *dev)
{
	dma_free_coherent(&dev->dev, MAX_SIZE, dst, dma_dst);
	//static inline void dma_free_coherent(struct device *dev, size_t size,void *cpu_addr, dma_addr_t dma_handle)
	of_reserved_mem_device_release(&dev->dev);
	dma_release_channel(vdma.rx_chan);
	#ifdef AUTO_TRIGGER
	hrtimer_cancel(&vdma.htimer);
	#endif
	vdma_exit(&vdma);	
	printk("VDMA0 remove!\r\n");
	return 0;
}

//驱动入口函数
static int __init dma_init(void)
{
	//注册platform驱动
	platform_driver_register(&vdma_driver);
	return 0;
}

//驱动出口函数
static void __exit dma_exit(void)
{
	platform_driver_unregister(&vdma_driver);
}

module_init(dma_init);
module_exit(dma_exit);

/* 驱动描述信息 */  
MODULE_AUTHOR("LONGMEN");   
MODULE_LICENSE("GPL"); 